#include "ThreadCache.h"
#include "CentralCache.h"

void* ThreadCache::Allocate(size_t size)
{
	assert(size <= MAX_BYTES);
	size_t alignSize = SizeClass::RoundUp(size);
	size_t index = SizeClass::Index(size);

	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();
	}
	else
	{
		return FetchFromCentralCache(index, alignSize);
	}
}

void ThreadCache::Deallocate(void*& ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	// 找对映射的自由链表桶，对象插入进入
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);

	//当桶里的数据大于能申请到的最大值时,就还回去一部分
	//例如当我们申请到100的内容后,后面再不断地给上层使用拿走再还回来,当还回来的数量大于100时,就把一些还给Centralcache
	//(由于是慢增长,当申请到100后肯定有一部分是还没被还回来的)
	if (_freeLists[index].Size() >= _freeLists[index].Maxsize())
	{
		ListTooLong(_freeLists[index], size);
	}

	ptr = nullptr;
}

void ThreadCache::ListTooLong(Freelist& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	//将Maxsize个小内存块还给Centralcache
	list.PopRange(start, end, list.Maxsize()); //先删除自由链表上的小内存块
	CentralCache::GetInStance()->ReleaseListToSpans(start, nullptr, size);
}

// 从中心缓存获得内存,要传桶的号数,因为中心缓存也使用了相同的哈希桶结构,只是挂的内容不一样
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	// 慢开始反馈调节算法
	// 从1开始,一直到NumMoveSize,慢慢增加到能获得的上限
	// 1、最开始不会一次向central cache要太多，因为可能会浪费
	// 2、如果不要这个size的默认大小内存需求，那么batchNum就会不断增长一直到上限
	// 3、size越大,一次向central cache要的batchNum就越小
	// 4、size越小,一次向central cache要的batchNum就越大(慢慢增长)

	// 每个桶都有一个maxsize,当maxsize比NumMoveSize的数小,就给maxsize个内存块,大了就使用另外一个参数
#ifdef _WIN32
	// 当包含windows.h头文件时,里面有一个自带的min
	size_t batchNum = min(_freeLists[index].Maxsize(), SizeClass::NumMoveSize(size));
#else
	size_t batchNum = std::min(_freeLists[index].Maxsize(), SizeClass::NumMoveSize(size));
#endif

	if (_freeLists[index].Maxsize() == batchNum)
	{
		// 让maxsize慢慢增大
		_freeLists[index].Maxsize() += 1;
	}

	void* start = nullptr;
	void* end = nullptr;

	// start和end都是输出型参数
	size_t actualNum = CentralCache::GetInStance()->FetchRangeObj(start, end, batchNum, size);
	// 当span里剩余的小内存块不够了,但是我还是要,就会返回我获得的小内存块数量
	assert(actualNum > 0);

	if (actualNum == 1)
	{
		assert(start == end);

		return start;
	}
	else
	{
		// 要把获得的链表的第二个节点挂到自由链表上
		// 我们获得的第一个节点start必定是一个size对齐后的数,而start后面的都是属于多要的一部分
		// 将这多要的一部分放进自由链表,以便后面thread cache进行无锁访问,而不用再向中心缓存进行有锁访问
		_freeLists[index].PushRange(Nextobj(start), end, actualNum - 1);
		return start;
	}

	return nullptr;
}